home *** CD-ROM | disk | FTP | other *** search
/ Java Developer's Companion / Java Developer's Companion.iso / documentation / tutorial / example / ImageMap.java < prev    next >
Encoding:
Java Source  |  1997-07-13  |  11.0 KB  |  417 lines

  1. /*
  2.  * Copyright (c) 1995-1997 Sun Microsystems, Inc. All Rights Reserved.
  3.  *
  4.  * Permission to use, copy, modify, and distribute this software
  5.  * and its documentation for NON-COMMERCIAL purposes and without
  6.  * fee is hereby granted provided that this copyright notice
  7.  * appears in all copies. Please refer to the file "copyright.html"
  8.  * for further important copyright and licensing information.
  9.  *
  10.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  11.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  12.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  13.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  14.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  15.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  16.  */
  17. /*
  18.  * @(#)ImageMap.java    1.4 95/10/13  
  19.  *
  20.  * Copyright (c) 1994-1995 Sun Microsystems, Inc. All Rights Reserved.
  21.  *
  22.  * Permission to use, copy, modify, and distribute this software
  23.  * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
  24.  * without fee is hereby granted. 
  25.  * Please refer to the file http://java.sun.com/copy_trademarks.html
  26.  * for further important copyright and trademark information and to
  27.  * http://java.sun.com/licensing.html for further important licensing
  28.  * information for the Java (tm) Technology.
  29.  * 
  30.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  31.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  32.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  33.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  34.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  35.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  36.  * 
  37.  * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
  38.  * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
  39.  * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
  40.  * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
  41.  * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
  42.  * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
  43.  * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES").  SUN
  44.  * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
  45.  * HIGH RISK ACTIVITIES.
  46.  */
  47.  
  48. import java.applet.Applet;
  49. import java.awt.Image;
  50. import java.awt.Graphics;
  51. import java.awt.Rectangle;
  52. import java.util.StringTokenizer;
  53. import java.util.Vector;
  54. import java.util.Hashtable;
  55. import java.net.URL;
  56. import java.awt.image.*;
  57. import java.net.MalformedURLException;
  58.  
  59. /**
  60.  * An extensible ImageMap applet class.
  61.  * The active areas on the image are controlled by ImageArea classes
  62.  * that can be dynamically loaded over the net.
  63.  *
  64.  * @author     Jim Graham
  65.  * @version     1.4, 13 Oct 1995
  66.  */
  67. public class ImageMap extends Applet implements Runnable {
  68.     /**
  69.      * The unhighlighted image being mapped.
  70.      */
  71.     Image baseImage;
  72.  
  73.     /**
  74.      * The list of image area handling objects;
  75.      */
  76.     ImageMapArea areas[];
  77.  
  78.     /**
  79.      * The primary highlight mode to be used.
  80.      */
  81.     static final int BRIGHTER = 0;
  82.     static final int DARKER = 1;
  83.  
  84.     int hlmode = BRIGHTER;
  85.  
  86.     /**
  87.      * The percentage of highlight to apply for the primary highlight mode.
  88.      */
  89.     int hlpercent = 50;
  90.  
  91.     /**
  92.      * Get a rectangular region of the baseImage highlighted according to
  93.      * the primary highlight specification.
  94.      */
  95.     Image getHighlight(int x, int y, int w, int h) {
  96.     return getHighlight(x, y, w, h, hlmode, hlpercent);
  97.     }
  98.  
  99.     /**
  100.      * Get a rectangular region of the baseImage with a specific highlight.
  101.      */
  102.     Image getHighlight(int x, int y, int w, int h, int mode, int percent) {
  103.     return getHighlight(x, y, w, h, new HighlightFilter(mode == BRIGHTER,
  104.                                 percent));
  105.     }
  106.  
  107.     /**
  108.      * Get a rectangular region of the baseImage modified by an image filter.
  109.      */
  110.     Image getHighlight(int x, int y, int w, int h, ImageFilter filter) {
  111.     Image cropped = makeImage(baseImage, new CropImageFilter(x, y, w, h));
  112.     return makeImage(cropped, filter);
  113.     }
  114.  
  115.     /**
  116.      * Make the primary highlighted version of the baseImage.
  117.      */
  118.     Image makeImage(Image orig, ImageFilter filter) {
  119.     return createImage(new FilteredImageSource(orig.getSource(), filter));
  120.     }
  121.  
  122.     /**
  123.      * Parse a string representing the desired highlight to be applied.
  124.      */
  125.     void parseHighlight(String s) {
  126.     if (s == null) {
  127.         return;
  128.     }
  129.     if (s.startsWith("brighter")) {
  130.         hlmode = BRIGHTER;
  131.         if (s.length() > "brighter".length()) {
  132.         hlpercent = Integer.parseInt(s.substring("brighter".length()));
  133.         }
  134.     } else if (s.startsWith("darker")) {
  135.         hlmode = DARKER;
  136.         if (s.length() > "darker".length()) {
  137.         hlpercent = Integer.parseInt(s.substring("darker".length()));
  138.         }
  139.     }
  140.     }
  141.  
  142.     /**
  143.      * Initialize the applet. Get attributes.
  144.      *
  145.      * Initialize the ImageAreas.
  146.      * Each ImageArea is a subclass of the class ImageArea, and is
  147.      * specified with an attribute of the form:
  148.      *         areaN=ImageAreaClassName,arguments...
  149.      * The ImageAreaClassName is parsed off and a new instance of that
  150.      * class is created.  The initializer for that class is passed a
  151.      * reference to the applet and the remainder of the attribute
  152.      * string, from which the class should retrieve any information it
  153.      * needs about the area it controls and the actions it needs to
  154.      * take within that area.
  155.      */
  156.     public void init() {
  157.     String s;
  158.  
  159.     parseHighlight(getParameter("highlight"));
  160.     introTune = getParameter("startsound");
  161.     baseImage = getImage(getDocumentBase(), getParameter("img"));
  162.     Vector areaVec = new Vector();
  163.     int num = 1;
  164.     while (true) {
  165.         ImageMapArea newArea;
  166.         s = getParameter("area"+num);
  167.         if (s == null) {
  168.         // Try rect for backwards compatibility.
  169.         s = getParameter("rect"+num);
  170.         if (s == null) {
  171.             break;
  172.         }
  173.         try {
  174.             newArea = new HighlightArea();
  175.             newArea.init(this, s);
  176.             areaVec.addElement(newArea);
  177.             String url = getParameter("href"+num);
  178.             if (url != null) {
  179.             s += "," + url;
  180.             newArea = new LinkArea();
  181.             newArea.init(this, s);
  182.             areaVec.addElement(newArea);
  183.             }
  184.         } catch (Exception e) {
  185.             System.out.println("error processing: "+s);
  186.             e.printStackTrace();
  187.             break;
  188.         }
  189.         } else {
  190.         try {
  191.             int classend = s.indexOf(",");
  192.             String name = s.substring(0, classend);
  193.             newArea = (ImageMapArea) Class.forName(name).newInstance();
  194.             s = s.substring(classend+1);
  195.             newArea.init(this, s);
  196.             areaVec.addElement(newArea);
  197.         } catch (Exception e) {
  198.             System.out.println("error processing: "+s);
  199.             e.printStackTrace();
  200.             break;
  201.         }
  202.         }
  203.         num++;
  204.     }
  205.     areas = new ImageMapArea[areaVec.size()];
  206.     areaVec.copyInto(areas);
  207.     checkSize();
  208.     }
  209.  
  210.     Thread aniThread = null;
  211.     String introTune = null;
  212.  
  213.     public void start() {
  214.     if (introTune != null)
  215.         try {
  216.         play(new URL(getDocumentBase(), introTune));
  217.         } catch (MalformedURLException e) {}
  218.     if (aniThread == null) {
  219.             aniThread = new Thread(this);
  220.             aniThread.setName("ImageMap Animator");
  221.             aniThread.start();
  222.     }
  223.     }
  224.  
  225.     public void run() {
  226.     Thread me = Thread.currentThread();
  227.     me.setPriority(Thread.MIN_PRIORITY);
  228.     for (int i = areas.length; --i >= 0; ) {
  229.         areas[i].getMedia();
  230.     }
  231.     while (aniThread == me) {
  232.         boolean animating = false;
  233.         for (int i = areas.length; --i >= 0; ) {
  234.         animating = areas[i].animate() || animating;
  235.         }
  236.         try {
  237.         synchronized(this) {
  238.             wait(animating ? 100 : 0);
  239.         }
  240.         } catch (InterruptedException e) {
  241.         break;
  242.         }
  243.     }
  244.     }
  245.  
  246.     public synchronized void startAnimation() {
  247.     notify();
  248.     }
  249.  
  250.     public synchronized void stop() {
  251.     aniThread = null;
  252.     notify();
  253.     }
  254.  
  255.     /**
  256.      * Check the size of this applet while the image is being loaded.
  257.      */
  258.     void checkSize() {
  259.     int w = baseImage.getWidth(this);
  260.     int h = baseImage.getHeight(this);
  261.     if (w > 0 && h > 0) {
  262.         resize(w, h);
  263.         synchronized(this) {
  264.         fullrepaint = true;
  265.         }
  266.         repaint(0, 0, w, h);
  267.     }
  268.     }
  269.  
  270.     private boolean fullrepaint = false;
  271.     private final static long UPDATERATE = 100;
  272.  
  273.     /**
  274.      * Handle updates from images being loaded.
  275.      */
  276.     public boolean imageUpdate(Image img, int infoflags,
  277.                    int x, int y, int width, int height) {
  278.     if ((infoflags & (WIDTH | HEIGHT)) != 0) {
  279.         checkSize();
  280.     }
  281.     if ((infoflags & (SOMEBITS | FRAMEBITS | ALLBITS)) != 0) {
  282.         synchronized(this) {
  283.         fullrepaint = true;
  284.         }
  285.         repaint(((infoflags & (FRAMEBITS | ALLBITS)) != 0)
  286.             ? 0 : UPDATERATE,
  287.             x, y, width, height);
  288.     }
  289.     return (infoflags & (ALLBITS | ERROR)) == 0;
  290.     }
  291.  
  292.     /**
  293.      * Paint the image and all active highlights.
  294.      */
  295.     public void paint(Graphics g) {
  296.     synchronized(this) {
  297.         fullrepaint = false;
  298.     }
  299.     if (baseImage == null) {
  300.         return;
  301.     }
  302.     g.drawImage(baseImage, 0, 0, this);
  303.     if (areas != null) {
  304.         for (int i = areas.length; --i >= 0; ) {
  305.         areas[i].highlight(g);
  306.         }
  307.     }
  308.     }
  309.  
  310.     /**
  311.      * Update the active highlights on the image.
  312.      */
  313.     public void update(Graphics g) {
  314.     boolean full;
  315.     synchronized(this) {
  316.         full = fullrepaint;
  317.     }
  318.     if (full) {
  319.         paint(g);
  320.         return;
  321.     }
  322.     if (baseImage == null) {
  323.         return;
  324.     }
  325.     g.drawImage(baseImage, 0, 0, this);
  326.     if (areas == null) {
  327.         return;
  328.     }
  329.     // First unhighlight all of the deactivated areas
  330.     for (int i = areas.length; --i >= 0; ) {
  331.         areas[i].highlight(g);
  332.     }
  333.     }
  334.  
  335.     /**
  336.      * Make sure that no ImageAreas are highlighted.
  337.      */
  338.     public void mouseExit() {
  339.     for (int i = 0; i < areas.length; i++) {
  340.         areas[i].exit();
  341.     }
  342.     }
  343.  
  344.     /**
  345.      * Find the ImageAreas that the mouse is in.
  346.      */
  347.     public boolean mouseMove(java.awt.Event evt, int x, int y) {
  348.     boolean eaten = false;
  349.  
  350.     for (int i = 0; i < areas.length; i++) {
  351.         if (!eaten && areas[i].inside(x, y)) {
  352.         eaten = areas[i].checkEnter(x, y);
  353.         } else {
  354.         areas[i].checkExit();
  355.         }
  356.     }
  357.  
  358.     return true;
  359.     }
  360.  
  361.     int pressX;
  362.     int pressY;
  363.  
  364.     /**
  365.      * Inform all active ImageAreas of a mouse press.
  366.      */
  367.     public boolean mouseDown(java.awt.Event evt, int x, int y) {
  368.     pressX = x;
  369.     pressY = y;
  370.  
  371.     for (int i = 0; i < areas.length; i++) {
  372.         if (areas[i].inside(x, y)) {
  373.         if (areas[i].press(x, y)) {
  374.             break;
  375.         }
  376.         }
  377.     }
  378.  
  379.     return true;
  380.     }
  381.  
  382.     /**
  383.      * Inform all active ImageAreas of a mouse release.
  384.      * Only those areas that were inside the original mouseDown()
  385.      * are informed of the mouseUp.
  386.      */
  387.     public boolean mouseUp(java.awt.Event evt, int x, int y) {
  388.     for (int i = 0; i < areas.length; i++) {
  389.         if (areas[i].inside(pressX, pressY)) {
  390.         if (areas[i].lift(x, y)) {
  391.             break;
  392.         }
  393.         }
  394.     }
  395.  
  396.     return true;
  397.     }
  398.  
  399.     /**
  400.      * Inform all active ImageAreas of a mouse drag.
  401.      * Only those areas that were inside the original mouseDown()
  402.      * are informed of the mouseUp.
  403.      */
  404.     public boolean mouseDrag(java.awt.Event evt, int x, int y) {
  405.     mouseMove(evt, x, y);
  406.     for (int i = 0; i < areas.length; i++) {
  407.         if (areas[i].inside(pressX, pressY)) {
  408.         if (areas[i].drag(x, y)) {
  409.             break;
  410.         }
  411.         }
  412.     }
  413.  
  414.     return true;
  415.     }
  416. }
  417.